package com.tjbank.cssys.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;

public class SM3Digest {

    /** SM3值的长度 */
    private static final int BYTE_LENGTH = 32;
    /** SM3分组长度 */
    private static final int BLOCK_LENGTH = 64;
    /** 缓冲区长度 */
    private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1;
    /** 缓冲区 */
    private byte[] xBuf = new byte[BUFFER_LENGTH];
    /** 缓冲区偏移量 */
    private int xBufOff;
    /** 初始向量 */
    private byte[] V = SM3.iv.clone();
    private int cntBlock = 0;
    private static final Logger logger = LoggerFactory.getLogger(SM3Digest.class);
    public SM3Digest() {
    }

    public SM3Digest(SM3Digest t) {
        System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length);
        this.xBufOff = t.xBufOff;
        System.arraycopy(t.V, 0, this.V, 0, t.V.length);
    }

    /** * SM3结果输出 * * @param out 保存SM3结构的缓冲区 * @param outOff 缓冲区偏移量 * @return */
    public int doFinal(byte[] out, int outOff) {
        byte[] tmp = doFinal();
        System.arraycopy(tmp, 0, out, 0, tmp.length);
        return BYTE_LENGTH;
    }

    public void reset() {
        xBufOff = 0;
        cntBlock = 0;
        V = SM3.iv.clone();
    }

    /**
     * * 明文输入 * * @param in * 明文输入缓冲区 * @param inOff * 缓冲区偏移量 * @param len * 明文长度
     */
    public void update(byte[] in, int inOff, int len) {
        int partLen = BUFFER_LENGTH - xBufOff;
        int inputLen = len;
        int dPos = inOff;
        if (partLen < inputLen) {
            System.arraycopy(in, dPos, xBuf, xBufOff, partLen);
            inputLen -= partLen;
            dPos += partLen;
            doUpdate();
            while (inputLen > BUFFER_LENGTH) {
                System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH);
                inputLen -= BUFFER_LENGTH;
                dPos += BUFFER_LENGTH;
                doUpdate();
            }
        }
        System.arraycopy(in, dPos, xBuf, xBufOff, inputLen);
        xBufOff += inputLen;
    }

    private void doUpdate() {
        byte[] B = new byte[BLOCK_LENGTH];
        for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) {
            System.arraycopy(xBuf, i, B, 0, B.length);
            doHash(B);
        }
        xBufOff = 0;
    }

    private void doHash(byte[] B) {
        byte[] tmp = SM3.CF(V, B);
        System.arraycopy(tmp, 0, V, 0, V.length);
        cntBlock++;
    }

    private byte[] doFinal() {
        byte[] B = new byte[BLOCK_LENGTH];
        byte[] buffer = new byte[xBufOff];
        System.arraycopy(xBuf, 0, buffer, 0, buffer.length);
        byte[] tmp = SM3.padding(buffer, cntBlock);
        for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) {
            System.arraycopy(tmp, i, B, 0, B.length);
            doHash(B);
        }
        return V;
    }

    public void update(byte in) {
        byte[] buffer = new byte[] { in };
        update(buffer, 0, 1);
    }

    public int getDigestSize() {
        return BYTE_LENGTH;
    }

    /**
     * 将二进制转换成16进制
     *
     * @param buf
     * @return
     */
    public static String byte2HexStr(byte buf[]) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     *
     * Convert hex string to byte[]
     * @param hexString   the hex string
     *
     * @return byte[]
     *
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    /**
     *
     * Convert char to byte
     *
     * @param c   char
     * @return byte
     *
     */
    public static byte charToByte(char c){
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    public static byte[] hash(byte[] srcData){
        SM3Digest sm3 = new SM3Digest();
        sm3.update(srcData, 0, srcData.length);
        byte[] hash = new byte[sm3.getDigestSize()];
        sm3.doFinal(hash,0);
        return hash;
    }

    public static boolean verify(String srcStr,String sm3HexStr){
        boolean flag = false;
        try{
            byte[] srcBytes = srcStr.getBytes();
            byte[] sm3Bytes = hexStringToBytes(sm3HexStr);
            byte[] srcSm3Hash = hash(srcBytes);
            if(Arrays.equals(srcSm3Hash,sm3Bytes)){
                flag = true;
            }

        }catch (Exception e){
            logger.error("SM3加密异常",e.getMessage());
        }

        return flag;
    }


    public static void main(String[] args) {
        byte[] md = new byte[32];
        byte[] msg1 = "ererfeiisgod".getBytes();
        SM3Digest sm3 = new SM3Digest();
        sm3.update(msg1, 0, msg1.length);
        sm3.doFinal(md, 0);
        String s = new String(byte2HexStr(md));
        System.out.println(s.toUpperCase());
        System.out.println("-------------------");
        boolean ererfeiisgod = verify("ererfeiisgod", s);
        System.out.println("ererfeiisgod==="+ererfeiisgod);


        SM3Digest sm3d = new SM3Digest();
        sm3d.update(msg1, 0, msg1.length);
        sm3d.doFinal(md, 0);
        String ss = new String(byte2HexStr(md));
        System.out.println(ss.toUpperCase());

    }


}
